//FACEBOOK CLIENT CLASS IMPLEMENTATION

#include "facebook_new.h"

#include "xmlParser.h"
#include "md5.h"
#include <fstream>

//Constructor and destructor
//==========================================================================

Facebook::Facebook()
{

	//main data reset
	m_AppKey = "";
	m_AppSecret = "";
	m_ApiServer = "";
	m_LoginURL = "";
	m_CallID = 0;
	m_pCallbackFunc = NULL;

	//error description reset
	m_ErrorDescription = "";
	m_FacebookSiteChanged = false;
	m_InvalidLoginPassword = false;

	//users reset
	m_UsersList.clear();
	m_pCurUser = NULL;



}//Facebook::Facebook()



Facebook::~Facebook()
{
	//cleaning users list
	std::list<FacebookUser*>::iterator Iter;

	if (m_UsersList.empty() == false)
	{
		for (Iter = m_UsersList.begin(); Iter != m_UsersList.end(); Iter++)
		{
			FacebookUser* TmpUser = (*Iter);

			if (TmpUser != NULL)
			{
				//if user was logged in - then logout
				if (TmpUser->m_LoggedIn == true)
					Helper_LogoutUser(TmpUser);

				//deleting user
				delete TmpUser;
			}
		}
	}

}//Facebook::~Facebook()




//curl progress callback function
//==========================================================================

int Facebook::ProgressCallback(void *clientp, double dltotal, double dlnow, double ultotal, double ulnow)
{

	if (Instance().m_pCallbackFunc != NULL)
		Instance().m_pCallbackFunc();

	return 0;
}




//Init
//==========================================================================

bool Facebook::Init(const std::string& AppKey, const std::string& AppSecret, const std::string& ApiServer, const std::string& LoginURL)
{
	//checking if this method was already called.
	if (m_AppKey != "")
	{
		m_ErrorDescription = "Facebook::Init(). Method was already called! It must be called only once.";
		return false;
	}

	//storing passed params
	m_AppKey = AppKey;
	m_AppSecret = AppSecret;
	m_ApiServer = ApiServer;
	m_LoginURL = LoginURL;

	//validating params
	if (m_AppKey.size() < 1 || m_AppSecret.size() < 1 || m_ApiServer.size() < 1 || m_LoginURL.size() < 1)
	{
		m_ErrorDescription = "Facebook::Init(). Invalid params were passed!";
		return false;
	}



	//returning success
	m_ErrorDescription = "";
	return true;

}//bool Facebook::Init(const std::string& AppKey, const std::string& AppSecret, const std::string& ApiServer, const std::string& LoginURL)




//Working with users
//==========================================================================

bool Facebook::SetCurrentUser(const std::string& UserEmail, const std::string& UserPassword)
{
	//checking if init wasn't called
	if (m_AppKey.size() < 1)
	{
		m_ErrorDescription = "Facebook::SetCurrentUser(). Facebook::Init() wasn't called!";
		return false;
	}


	//validating params
	if (UserEmail.size() < 1 || UserPassword.size() < 1)
	{
		m_ErrorDescription = "Facebook::SetCurrentUser(). Invalid params passed!";
		return false;
	}

	//resetting current user
	m_pCurUser = NULL;

	bool MustAddUser = true;

	if (m_UsersList.empty() == false)
	{
		//checking if user is already in the list
		std::list<FacebookUser*>::iterator Iter;

		for (Iter = m_UsersList.begin(); Iter != m_UsersList.end(); Iter++)
		{
			if ((*Iter) == NULL)
			{
				m_ErrorDescription = "Facebook::SetCurrentUser(). Internal error: NULL pointer in users list!";
				return false;
			}

			if ((*Iter)->m_UserEmail == UserEmail)
			{
				//user found.
				m_pCurUser = (*Iter);

				//checking for weird error
				if (m_pCurUser->m_LoggedIn == true && m_pCurUser->m_UserPassword != UserPassword)
				{
					m_ErrorDescription = "Facebook::SetCurrentUser(). User already logged in with different password! Weird!";
					return false;
				}

				//remembering the new password
				m_pCurUser->m_UserPassword = UserPassword;

				MustAddUser = false;
				break;
			}
		}
	}

	//creating new user if needed
	if (MustAddUser == true)
	{
		m_pCurUser = new FacebookUser(UserEmail, UserPassword);

		//setting callback func for this new user
		curl_easy_setopt(m_pCurUser->m_pCurl, CURLOPT_PROGRESSFUNCTION, ProgressCallback); 

		//putting current user in the list
		m_UsersList.push_back(m_pCurUser);
	}


	//success
	m_ErrorDescription = "";
	return true;


}//bool Facebook::SetCurrentUser(const std::string& UserEmail, const std::string& UserPassword)



bool Facebook::RemoveCurrentUser()
{
	if (m_pCurUser == NULL)
	{
		m_ErrorDescription = "Facebook::RemoveCurrentUser(). Current user was not set!";
		return false;
	}

	//if current user was logged in - making log out.
	if (m_pCurUser->m_LoggedIn == true)
		Helper_LogoutUser(m_pCurUser);

	//removing user from users list
	std::list<FacebookUser*>::iterator Iter;
	bool UserWasFound = false;

	for (Iter = m_UsersList.begin(); Iter != m_UsersList.end(); Iter++)
	{
		if ((*Iter) == m_pCurUser)
		{
			m_UsersList.erase(Iter);
			UserWasFound = true;
			break;
		}
	}

	if (UserWasFound == false)
	{
		m_ErrorDescription = "Facebook::RemoveCurrentUser(). Internal error: Cur user wasn't found in users list!";
		return false;
	}

	//deletting cur cur user
	delete m_pCurUser;
	m_pCurUser = NULL;

	//success
	m_ErrorDescription = "";
	return true;

}//bool Facebook::RemoveCurrentUser()





//Helper methods
//==========================================================================

void Facebook::Helper_LogoutUser(FacebookUser* pUser)
{
	assert(pUser != NULL);

	//preparing curl

	curl_easy_reset(pUser->m_pCurl);

	//setting url
	curl_easy_setopt(pUser->m_pCurl, CURLOPT_URL, FACEBOOK_LOGOUTURL);

	//setting POST params
	curl_easy_setopt(pUser->m_pCurl, CURLOPT_POST, true );
	curl_easy_setopt(pUser->m_pCurl, CURLOPT_POSTFIELDS, "confirm=1");

	//enabling cookies
	curl_easy_setopt(pUser->m_pCurl, CURLOPT_COOKIEFILE, "");

	//enabling redirections
	curl_easy_setopt(pUser->m_pCurl, CURLOPT_FOLLOWLOCATION, 1);
	curl_easy_setopt(pUser->m_pCurl, CURLOPT_MAXREDIRS, 100);

	//setting user agent - a subject to think about
	curl_easy_setopt(pUser->m_pCurl, CURLOPT_USERAGENT, FACEBOOK_USERAGENT);
	//setting callback func for this new user
	curl_easy_setopt(pUser->m_pCurl, CURLOPT_NOPROGRESS, false);
	curl_easy_setopt(pUser->m_pCurl, CURLOPT_PROGRESSFUNCTION, ProgressCallback); 

	//performing request
	CURLcode RequestResult = curl_easy_perform(pUser->m_pCurl);

}//void Facebook::Helper_LogoutUser(FacebookUser* pUser)



bool Facebook::Helper_CreateSession()
{
	assert (m_pCurUser != NULL);

	std::list<std::string> Params;
	std::string XmlResult;

	Params.push_back("auth_token=" + m_pCurUser->m_LoginToken);

	bool RequestResult = HelperRequest_CallAPIMethod("facebook.auth.getSession", Params, XmlResult); 

	if (RequestResult == false || XmlResult.size() < 1)
	{
		m_ErrorDescription = "Facebook::Helper_CreateSession(). Request failed!";
		return false;
	}

	XMLNode Head = XMLNode::parseString(XmlResult.c_str( ), NULL);
	XMLNode Result = Head.getChildNode(_T("auth_getSession_response"));

	//session key
	XMLNode Node = Result.getChildNode(_T("session_key"));

	if (Node.isEmpty())
	{
		m_ErrorDescription = "Facebook::Helper_CreateSession(). Session key wasn't passed!";
		return false;
	}

	m_pCurUser->m_LoginSession = Node.getText();

	//session secret
	Node = Result.getChildNode(_T("secret"));

	if (Node.isEmpty())
	{
		m_ErrorDescription = "Facebook::Helper_CreateSession(). Session secret wasn't passed!";
		return false;
	}

	m_pCurUser->m_SessionSecret = Node.getText();

	//user id
	Node = Result.getChildNode(_T("uid"));

	if (Node.isEmpty())
	{
		m_ErrorDescription = "Facebook::Helper_CreateSession(). User id wasn't passed!";
		return false;
	}

	m_pCurUser->m_UserID = Node.getText();

	//success
	m_ErrorDescription = "";
	m_pCurUser->m_LoggedIn = true;
	return true;

}//bool Facebook::Helper_CreateSession()



void Facebook::Helper_ParseProfileInfo(XMLNode& UserInfoNode, FacebookProfileInfo& Result)
{

	XMLNode CurNode;
	XMLNode ChildNode;

	//name
	CurNode = UserInfoNode.getChildNode(_T("name"));
	if (CurNode.getText() != NULL)
		Result.Name = CurNode.getText();

	//first_name
	CurNode = UserInfoNode.getChildNode(_T("first_name"));
	if (CurNode.getText() != NULL)
		Result.FirstName = CurNode.getText();

	//birthday
	CurNode = UserInfoNode.getChildNode(_T("birthday"));
	if (CurNode.getText() != NULL)
		Result.Birthday = CurNode.getText();

	//sex
	CurNode = UserInfoNode.getChildNode(_T("sex"));
	if (CurNode.getText() != NULL)
		Result.Sex = CurNode.getText();

	//is_app_user
	CurNode = UserInfoNode.getChildNode(_T("is_app_user"));
	if (CurNode.getText() != NULL)
		Result.IsAppUser = CurNode.getText();

	//status
	CurNode = UserInfoNode.getChildNode(_T("status"));
	ChildNode = CurNode.getChildNode(_T("message"));
	if (ChildNode.getText() != NULL)
		Result.Status = ChildNode.getText();

	//timezone
	CurNode = UserInfoNode.getChildNode(_T("timezone"));
	if (CurNode.getText() != NULL)
		Result.Timezone = CurNode.getText();

	//hometown_location: city and country
	CurNode = UserInfoNode.getChildNode(_T("hometown_location"));
	
	ChildNode = CurNode.getChildNode(_T("city"));
	if (ChildNode.getText() != NULL)
		Result.City = ChildNode.getText();

	ChildNode = CurNode.getChildNode(_T("country"));
	if (ChildNode.getText() != NULL)
		Result.Country = ChildNode.getText();

	//photo url
	Result.PhotoURL = "";

	CurNode = UserInfoNode.getChildNode(_T("pic"));
	if (CurNode.getText() != NULL)
	{
		Result.PhotoURL = CurNode.getText();
	}
	else
	{
		CurNode = UserInfoNode.getChildNode(_T("pic_small"));
		if (CurNode.getText() != NULL)
		{
			Result.PhotoURL = CurNode.getText();
		}
		else
		{
			CurNode = UserInfoNode.getChildNode(_T("pic_square"));
			if (CurNode.getText() != NULL)
			{
				Result.PhotoURL = CurNode.getText();
			}
			else
			{
				CurNode = UserInfoNode.getChildNode(_T("pic_big"));
				if (CurNode.getText() != NULL)
					Result.PhotoURL = CurNode.getText();
			}
		}
	}

	//photo size
	Result.PhotoSize.Width = 0;
	Result.PhotoSize.Height =0;

	if (Result.PhotoURL.size() > 0)
	{
		Helper_GetPhotoSize(Result.PhotoURL, Result.PhotoSize); 
	}

}//void Facebook::Helper_ParseProfileInfo(XMLNode& UserInfoNode, FacebookProfileInfo& Result)



void Facebook::Helper_GetPhotoSize(const std::string& PhotoURL, FacebookPhotoSize& Result)
{
	assert(PhotoURL.size() > 0);
	
	//creating curl easy handle
	CURL* pTempCurl = curl_easy_init();

	//resetting the curl
	curl_easy_reset(pTempCurl);

	//providing url to curl
	std::string ScriptURL = FACEBOOK_IMAGESIZESCRIPTURL;
	ScriptURL.append("?ImageURL=" + PhotoURL);
	curl_easy_setopt(pTempCurl, CURLOPT_URL, ScriptURL.c_str());

	//telling curl it will be a GET request
	curl_easy_setopt(pTempCurl, CURLOPT_HTTPGET, true);

	//setting the request result writing
	std::string RequestResult = "";

	curl_easy_setopt(pTempCurl, CURLOPT_WRITEFUNCTION, Facebook::Write_Callback);
    curl_easy_setopt(pTempCurl, CURLOPT_WRITEDATA, &RequestResult);	
	//setting callback func for this new user
	curl_easy_setopt(pTempCurl, CURLOPT_NOPROGRESS, false);
	curl_easy_setopt(pTempCurl, CURLOPT_PROGRESSFUNCTION, ProgressCallback); 

	//making the request
	CURLcode RequestResultCode;
	RequestResultCode = curl_easy_perform(pTempCurl);

	//checking if request was ok
	if (RequestResultCode == 0 || RequestResult.size() < 12)
	{
		//parsing result
		std::string::size_type FindAtLoc = RequestResult.find("@");

		if (FindAtLoc != std::string::npos)
		{
			std::string Width = RequestResult.substr(0, FindAtLoc);
			std::string Height= RequestResult.substr(FindAtLoc + 1, RequestResult.size() - FindAtLoc - 1);

			Result.Width = atoi(Width.c_str());
			Result.Height= atoi(Height.c_str());
		}
	}

	//releasing curl handle
	curl_easy_cleanup(pTempCurl);

}//void Facebook::Helper_GetPhotoSize(const std::string& PhotoURL, FacebookPhotoSize& Result)





void Facebook::Helper_FillInfoFieldFromProfile(FACEBOOK_PROFILEINFO InfoField, const FacebookProfileInfo& ProfileInfo, std::string& Result)
{
	Result = "";

	switch(InfoField)
	{
	case FBPROFILEINFO_NAME:	  Result = ProfileInfo.Name; return;
	case FBPROFILEINFO_FIRSTNAME: Result = ProfileInfo.FirstName; return;
	case FBPROFILEINFO_BIRTHDAY:  Result = ProfileInfo.Birthday; return;
	case FBPROFILEINFO_SEX:		  Result = ProfileInfo.Sex; return;
	case FBPROFILEINFO_ISAPPUSER: Result = ProfileInfo.IsAppUser; return;
	case FBPROFILEINFO_STATUS:	  Result = ProfileInfo.Status; return;
	case FBPROFILEINFO_TIMEZONE:  Result = ProfileInfo.Timezone; return;
	case FBPROFILEINFO_CITY:	  Result = ProfileInfo.City; return;
	case FBPROFILEINFO_COUNTRY:   Result = ProfileInfo.Country; return;
	case FBPROFILEINFO_PHOTOURL:  Result = ProfileInfo.PhotoURL; return;
	}

}//void Facebook::Helper_FillInfoFieldFromProfile(FACEBOOK_PROFILEINFO InfoField, const FacebookProfileInfo& ProfileInfo, std::string& Result)




bool Facebook::Helper_FetchPhoto(const std::string& PhotoURL, std::string& Result)
{
	//photo url correctness must be checked in calling method.

	//creating curl easy handle
	CURL* pTempCurl = curl_easy_init();

	//resetting the curl
	curl_easy_reset(pTempCurl);

	//providing url to curl
	curl_easy_setopt(pTempCurl, CURLOPT_URL, PhotoURL.c_str());

	//telling curl it will be a GET request
	curl_easy_setopt(pTempCurl, CURLOPT_HTTPGET, true);

	//setting the request result writing
	Result = "";

	curl_easy_setopt(pTempCurl, CURLOPT_WRITEFUNCTION, Facebook::Write_Callback);
    curl_easy_setopt(pTempCurl, CURLOPT_WRITEDATA, &Result);	
	//setting callback func for this new user
	curl_easy_setopt(pTempCurl, CURLOPT_NOPROGRESS, false);
	curl_easy_setopt(pTempCurl, CURLOPT_PROGRESSFUNCTION, ProgressCallback); 

	//making the request
	CURLcode RequestResultCode;
	RequestResultCode = curl_easy_perform(pTempCurl);

	//checking if request was ok
	if (RequestResultCode == 0)
		return true;
	else
		return false;

	//releasing curl handle
	curl_easy_cleanup(pTempCurl);

}//bool Facebook::Helper_FetchPhoto(const std::string& PhotoURL, std::string& Result)



void Facebook::Helper_BuildAppAndNonAppVectors(FacebookUser* pUser)
{

	assert(pUser != NULL);

	pUser->m_AppFriendsIndexes.clear();
	pUser->m_NonAppFriendsIndexes.clear();

	int FriendsCount = (int)pUser->m_FriendsProfileInfo.size();

	if (FriendsCount < 1)
		return;

	pUser->m_AppFriendsIndexes.reserve(FriendsCount);
	pUser->m_NonAppFriendsIndexes.reserve(FriendsCount);

	int i;

	for (i = 0; i < FriendsCount; i++)
	{
		if (pUser->m_FriendsProfileInfo.at(i).IsAppUser == "1")
			pUser->m_AppFriendsIndexes.push_back(i);
		else
			pUser->m_NonAppFriendsIndexes.push_back(i);
	}

}//void Facebook::Helper_BuildAppAndNonAppVectors(FacebookUser* pUser)



void Facebook::Helper_SaveRequestResultToFile(std::string& RequestResult)
{

	std::ofstream FileOut("request.txt", std::ios_base::trunc);

	FileOut.write(RequestResult.c_str(), RequestResult.size());

	FileOut.close();

}




//Helper Request methods
//==========================================================================

void Facebook::HelperRequest_MakeMD5(const std::string& SourceString, std::string& Result) 
{
	md5_state_t State;
	md5_byte_t Digest[16];
	char Hex_output[16*2 + 1];
	int di;
	md5_init( &State );
	md5_append( &State, (const md5_byte_t *) SourceString.c_str( ), (int) strlen( SourceString.c_str( ) ) );
	md5_finish( &State, Digest );
	for( di = 0; di < 16; ++di )
		sprintf( Hex_output+di * 2, "%02x", Digest[di] );
	
	Result = Hex_output;

}//void Facebook::HelperRequest_MakeMD5(const std::string& SourceString, std::string& Result) 



void Facebook::HelperRequest_GetParamsString(std::list<std::string>& Params, bool Separate, std::string& Result)
{

	std::list<std::string>::iterator Iter;
	
	Result = "";

	Params.sort();

	for(Iter = Params.begin(); Iter != Params.end(); Iter++)
	{
		Result.append(*Iter);
		if (Separate == true)
			Result.append("&");
	}
	if (Separate == true)
		Result.erase( Result.size() - 1, 1 );

}//void Facebook::HelperRequest_GetParamsString(const std::list<std::string>& Params, bool Separate)



void Facebook::HelperRequest_GetSignature(std::list<std::string>& Params, std::string& Result)
{

	Result = "";

	assert(m_pCurUser != NULL);

	HelperRequest_GetParamsString(Params, false, Result);

	if(m_pCurUser->m_SessionSecret.size() > 0)
		Result.append(m_pCurUser->m_SessionSecret);
	else
		Result.append(m_AppSecret);

	HelperRequest_MakeMD5(Result, Result);

}//void Facebook::HelperRequest_GetSignature(std::list<std::string>& Params, std::string& Result)



bool Facebook::HelperRequest_CallAPIMethod(const std::string& Method, std::list<std::string>& Params, std::string& Result)
{

	assert(m_pCurUser != NULL);
	CURL* pCurCurl = m_pCurUser->m_pCurl;
	assert(pCurCurl != NULL);

	//resetting curl state
	curl_easy_reset(pCurCurl);

	//setting url
	assert(m_ApiServer.size() > 0);
	curl_easy_setopt(pCurCurl, CURLOPT_URL, m_ApiServer.c_str());

	//adding standard params

	//callID - we just need to make sure that each callid is larger number than previous one
	m_CallID++;
	std::stringstream TempSStream;
	TempSStream << "call_id=" << m_CallID;
	Params.push_back(TempSStream.str());

	//api version we use
	std::string VersionNumber = "v=";
	VersionNumber.append(FACEBOOK_APIVER);
	Params.push_back(VersionNumber);

	//api key
	Params.push_back("api_key=" + m_AppKey);

	//method name as described in facebook reference documentation
	Params.push_back("method=" + Method);

	//session id
	if (m_pCurUser->m_LoginSession.size() > 0)
		Params.push_back("session_key=" + m_pCurUser->m_LoginSession);

	//generating and appending call signature
	std::string CallSig = "";
	HelperRequest_GetSignature(Params, CallSig); 
	Params.push_back("sig=" + CallSig);

	//generating params string
	std::string ParamsString = "";
	HelperRequest_GetParamsString(Params, true, ParamsString);

	//setting POST params
	curl_easy_setopt(pCurCurl, CURLOPT_POST, true );
	curl_easy_setopt(pCurCurl, CURLOPT_POSTFIELDS, ParamsString.c_str());

	//setting write result callback
	curl_easy_setopt(pCurCurl, CURLOPT_WRITEFUNCTION, Facebook::Write_Callback);
    curl_easy_setopt(pCurCurl, CURLOPT_WRITEDATA, &Result);	
	//setting callback func for this new user
	curl_easy_setopt(m_pCurUser->m_pCurl, CURLOPT_NOPROGRESS, false);
	curl_easy_setopt(m_pCurUser->m_pCurl, CURLOPT_PROGRESSFUNCTION, ProgressCallback); 

	//performing request
	CURLcode RequestResult = curl_easy_perform(pCurCurl);

	return (RequestResult == 0);

}//void Facebook::HelperRequest_CallAPIMethod(const std::string& Method, std::list<std::string>& Params, std::string& Result)






//Helper Request methods
//==========================================================================

size_t Facebook::Write_Callback(void *ptr, size_t size, size_t nmemb, void *userp)
{
	std::string *str = (std::string *) userp;
	str->append( (char *) ptr, size*nmemb );
	return size*nmemb;
}//size_t Facebook::Write_Callback(void *ptr, size_t size, size_t nmemb, void *userp)





//Working with current user
//==========================================================================

bool Facebook::CurUser_FetchUserInfo()
{
	//checking if init was called
	if (m_AppKey.size() < 1)
	{
		m_ErrorDescription = "Facebook::CurUser_FetchUserInfo(). Facebook::Init() wasn't called!"; return false;
	}

	//checking if cur user is ok
	if (m_pCurUser == NULL)
	{
		m_ErrorDescription = "Facebook::CurUser_FetchUserInfo(). No current user was set!"; return false;
	}

	if (m_pCurUser->m_LoggedIn == false)
	{
		m_ErrorDescription = "Facebook::CurUser_FetchUserInfo(). Current user is not logged in!"; return false;
	}

	if (m_pCurUser->m_UserProfileInfo.Name.size() > 0)
	{
		//info already fetched
		return true;
	}


	//no we may proceed
	std::list<std::string> Params;
	std::string XmlResult;
	
	Params.push_back("uids=" + m_pCurUser->m_UserID);
	Params.push_back("fields=" + std::string(FACEBOOK_PROFILEINFOQUERY)); 

	bool RequestResult = HelperRequest_CallAPIMethod("facebook.users.getInfo", Params, XmlResult); 

	if (RequestResult == false || XmlResult.size() < 1)
	{
		m_ErrorDescription = "Facebook::CurUser_FetchUserInfo(). Request failed!";
		return false;
	}

	XMLNode Head = XMLNode::parseString(XmlResult.c_str( ), NULL);
	XMLNode Result = Head.getChildNode(_T("users_getInfo_response"));

	//user node
	XMLNode UserNode = Result.getChildNode(_T("user"));

	//parsing profile info
	Helper_ParseProfileInfo(UserNode, m_pCurUser->m_UserProfileInfo);

    //checking if name is not empty
	if (m_pCurUser->m_UserProfileInfo.Name.size() > 0)
	{
		//success
		m_ErrorDescription = "";
		return true;
	}
	else
	{
		//failure
		m_ErrorDescription = "Facebook::CurUser_FetchUserInfo(). Request returned no data!";
		return false;
	}

}//bool Facebook::CurUser_FetchUserInfo()




bool Facebook::CurUser_FetchFriendsInfo()
{

	//checking if init was called
	if (m_AppKey.size() < 1)
	{
		m_ErrorDescription = "Facebook::CurUser_FetchFriendsInfo(). Facebook::Init() wasn't called!"; return false;
	}

	//checking if cur user is ok
	if (m_pCurUser == NULL)
	{
		m_ErrorDescription = "Facebook::CurUser_FetchFriendsInfo(). No current user was set!"; return false;
	}

	if (m_pCurUser->m_LoggedIn == false)
	{
		m_ErrorDescription = "Facebook::CurUser_FetchFriendsInfo(). Current user is not logged in!"; return false;
	}

	if (m_pCurUser->m_FriendsIDs.empty() == false)
	{
		//info already fetched
		return true;
	}


	//now we may proceed

	m_pCurUser->m_FriendsIDs.clear();
	m_pCurUser->m_FriendsProfileInfo.clear(); 
	m_pCurUser->m_FriendsPhotos.clear(); 


	std::list<std::string> Params;
	std::string XmlResult;

	//requesting friends IDs list first
	bool RequestResult = HelperRequest_CallAPIMethod("facebook.friends.get", Params, XmlResult); 

	if (RequestResult == false || XmlResult.size() < 1)
	{
		m_ErrorDescription = "Facebook::CurUser_FetchFriendsInfo(). facebook.friends.get request failed!";
		return false;
	}

	XMLNode Head = XMLNode::parseString(XmlResult.c_str( ), NULL);
	XMLNode Result = Head.getChildNode(_T("friends_get_response"));

	int FriendsCount = Result.nChildNode(_T("uid"));

	if (FriendsCount == 0)
	{
		//no need to continue - no friends
		m_ErrorDescription = "";
		return true;
	}

	//resizing vectors
	m_pCurUser->m_FriendsIDs.resize(FriendsCount);
	m_pCurUser->m_FriendsProfileInfo.resize(FriendsCount); 
	m_pCurUser->m_FriendsPhotos.resize(FriendsCount, ""); 

	//remembering friends ids
	XMLNode CurNode;

	int i;

	for (i = 0; i < FriendsCount; i++)
	{
		CurNode = Result.getChildNode(_T("uid"), FriendsCount - 1 - i);
		assert(!(CurNode.isEmpty()));
		m_pCurUser->m_FriendsIDs.at(FriendsCount - 1 - i) = CurNode.getText();
	}

	//now with friends ids we request their profiles information

	Params.clear();
	XmlResult = "";

	std::string FriendsIDsString = "uids=";

	for (i = 0; i < FriendsCount; i++)
	{
		FriendsIDsString.append(m_pCurUser->m_FriendsIDs.at(i));
		if (i != FriendsCount - 1)
			FriendsIDsString.append(",");
	}

	Params.push_back(FriendsIDsString);
	Params.push_back("fields=" + std::string(FACEBOOK_PROFILEINFOQUERY)); 

	RequestResult = HelperRequest_CallAPIMethod("facebook.users.getInfo", Params, XmlResult); 

	Head = XMLNode::parseString(XmlResult.c_str( ), NULL);
	Result = Head.getChildNode(_T("users_getInfo_response"));

	int FriendsInfoCount = Result.nChildNode(_T("user"));

	if (FriendsCount != FriendsInfoCount)
	{
		m_ErrorDescription = "Facebook::CurUser_FetchFriendsInfo(). facebook.users.getInfo request returned invalid number of entries!";
		return false;
	}

	XMLNode ChildNode;

	for (i = 0; i < FriendsInfoCount; i++)
	{
		CurNode = Result.getChildNode(_T("user"), FriendsInfoCount - 1 - i);
		assert(!(CurNode.isEmpty()));
		ChildNode = CurNode.getChildNode(_T("uid"));
		assert(!(ChildNode.isEmpty()));
		std::string CurUserID = ChildNode.getText();

		assert(CurUserID == m_pCurUser->m_FriendsIDs.at(FriendsInfoCount - 1 - i));

		Helper_ParseProfileInfo(CurNode, m_pCurUser->m_FriendsProfileInfo.at(FriendsInfoCount - 1 - i));
	}

	//building app and non-app friends indexes vectors
	Helper_BuildAppAndNonAppVectors(m_pCurUser);


	//success
	m_ErrorDescription = "";
	return true;

}//bool Facebook::CurUser_FetchFriendsInfo()




bool Facebook::CurUser_GetUserID(std::string& Result)
{
	Result = "";
	//checking if init was called
	if (m_AppKey.size() < 1)
	{
		m_ErrorDescription = "Facebook::CurUser_GetUserID(). Facebook::Init() wasn't called!"; return false;
	}

	//checking if cur user is ok
	if (m_pCurUser == NULL)
	{
		m_ErrorDescription = "Facebook::CurUser_GetUserID(). No current user was set!"; return false;
	}

	if (m_pCurUser->m_LoggedIn == false)
	{
		m_ErrorDescription = "Facebook::CurUser_GetUserID(). Current user is not logged in!"; return false;
	}

	Result = m_pCurUser->m_UserID;
	m_ErrorDescription = "";
	return true;

}//bool Facebook::CurUser_GetUserID(std::string& Result)



bool Facebook::CurUser_GetUserInfo(FACEBOOK_PROFILEINFO InfoField, std::string& Result)
{

	Result = "";
	//checking if init was called
	if (m_AppKey.size() < 1)
	{
		m_ErrorDescription = "Facebook::CurUser_GetUserInfo(). Facebook::Init() wasn't called!"; return false;
	}

	//checking if cur user is ok
	if (m_pCurUser == NULL)
	{
		m_ErrorDescription = "Facebook::CurUser_GetUserInfo(). No current user was set!"; return false;
	}

	if (m_pCurUser->m_LoggedIn == false)
	{
		m_ErrorDescription = "Facebook::CurUser_GetUserInfo(). Current user is not logged in!"; return false;
	}

	Helper_FillInfoFieldFromProfile(InfoField, m_pCurUser->m_UserProfileInfo, Result);  
	m_ErrorDescription = "";
	return true;

}//bool Facebook::CurUser_GetUserInfo(FACEBOOK_PROFILEINFO InfoField, std::string& Result)



int Facebook::CurUser_GetFriendsCount()
{
	if (m_pCurUser == NULL)
		return -1;

	if (m_pCurUser->m_FriendsIDs.empty() == true)
		return 0;
	else
		return (int) m_pCurUser->m_FriendsIDs.size();

}//int Facebook::CurUser_GetFriendsCount()



bool Facebook::CurUser_GetFriendID(int FriendIndex, std::string& Result)
{
	Result = "";
	//checking if init was called
	if (m_AppKey.size() < 1)
	{
		m_ErrorDescription = "Facebook::CurUser_GetFriendID(). Facebook::Init() wasn't called!"; return false;
	}

	//checking if cur user is ok
	if (m_pCurUser == NULL)
	{
		m_ErrorDescription = "Facebook::CurUser_GetFriendID(). No current user was set!"; return false;
	}

	if (m_pCurUser->m_LoggedIn == false)
	{
		m_ErrorDescription = "Facebook::CurUser_GetFriendID(). Current user is not logged in!"; return false;
	}

	if (FriendIndex < 0 || FriendIndex >= CurUser_GetFriendsCount())
	{
		m_ErrorDescription = "Facebook::CurUser_GetFriendID(). Invalid FriendIndex value!"; return false;
	}

    Result = m_pCurUser->m_FriendsIDs.at(FriendIndex);
	m_ErrorDescription = "";
	return true;

}//bool Facebook::CurUser_GetFriendID(int FriendIndex, std::string& Result)



bool Facebook::CurUser_GetFriendInfo(int FriendIndex, FACEBOOK_PROFILEINFO InfoField, std::string& Result)
{

	Result = "";
	//checking if init was called
	if (m_AppKey.size() < 1)
	{
		m_ErrorDescription = "Facebook::CurUser_GetFriendInfo(). Facebook::Init() wasn't called!"; return false;
	}

	//checking if cur user is ok
	if (m_pCurUser == NULL)
	{
		m_ErrorDescription = "Facebook::CurUser_GetFriendInfo(). No current user was set!"; return false;
	}

	if (m_pCurUser->m_LoggedIn == false)
	{
		m_ErrorDescription = "Facebook::CurUser_GetFriendInfo(). Current user is not logged in!"; return false;
	}

	if (FriendIndex < 0 || FriendIndex >= CurUser_GetFriendsCount())
	{
		m_ErrorDescription = "Facebook::CurUser_GetFriendInfo(). Invalid FriendIndex value!"; return false;
	}

    Helper_FillInfoFieldFromProfile(InfoField, m_pCurUser->m_FriendsProfileInfo.at(FriendIndex), Result); 
	m_ErrorDescription = "";
	return true;

}//bool Facebook::CurUser_GetFriendInfo(int FriendIndex, FACEBOOK_PROFILEINFO InfoField, std::string& Result)




bool Facebook::CurUser_FetchUserPhoto()
{

	//checking if init was called
	if (m_AppKey.size() < 1)
	{
		m_ErrorDescription = "Facebook::CurUser_FetchUserPhoto(). Facebook::Init() wasn't called!"; return false;
	}

	//checking if cur user is ok
	if (m_pCurUser == NULL)
	{
		m_ErrorDescription = "Facebook::CurUser_FetchUserPhoto(). No current user was set!"; return false;
	}

	//checking if photo is already fetched
	if (m_pCurUser->m_UserPhoto.size() > 0)
	{
		return true;
	}

	if (m_pCurUser->m_LoggedIn == false)
	{
		m_ErrorDescription = "Facebook::CurUser_FetchUserPhoto(). Current user is not logged in!"; return false;
	}

	//validating photo url
	if (m_pCurUser->m_UserProfileInfo.PhotoURL.size() < 1)
	{
		m_ErrorDescription = "Facebook::CurUser_FetchUserPhoto(). Current user photo URL is empty! Looks like he didn't set his profile picture"; return false;
	}

	//no we may proceed
    Helper_FetchPhoto(m_pCurUser->m_UserProfileInfo.PhotoURL, m_pCurUser->m_UserPhoto);   
	m_ErrorDescription = "";
	return true;

}//bool Facebook::CurUser_FetchUserPhoto()




bool Facebook::CurUser_FetchFriendPhoto(int FriendIndex)
{
	//checking if init was called
	if (m_AppKey.size() < 1)
	{
		m_ErrorDescription = "Facebook::CurUser_FetchFriendPhoto(). Facebook::Init() wasn't called!"; return false;
	}

	//checking if cur user is ok
	if (m_pCurUser == NULL)
	{
		m_ErrorDescription = "Facebook::CurUser_FetchFriendPhoto(). No current user was set!"; return false;
	}

	//checking if photo is already fetched
	if (m_pCurUser->m_FriendsPhotos.at(FriendIndex).size() > 0)
	{
		return true;
	}

	if (m_pCurUser->m_LoggedIn == false)
	{
		m_ErrorDescription = "Facebook::CurUser_FetchFriendPhoto(). Current user is not logged in!"; return false;
	}

	if (FriendIndex < 0 || FriendIndex >= CurUser_GetFriendsCount())
	{
		m_ErrorDescription = "Facebook::CurUser_FetchFriendPhoto(). Invalid FriendIndex value!"; return false;
	}

	//validating photo url
	if (m_pCurUser->m_FriendsProfileInfo.at(FriendIndex).PhotoURL.size() < 1)  
	{
		m_ErrorDescription = "Facebook::CurUser_FetchFriendPhoto(). Photo URL is empty! Looks like he didn't set his profile picture"; return false;
	}

	//no we may proceed
    Helper_FetchPhoto(m_pCurUser->m_FriendsProfileInfo.at(FriendIndex).PhotoURL, m_pCurUser->m_FriendsPhotos.at(FriendIndex));   
	m_ErrorDescription = "";
	return true;

}//bool Facebook::CurUser_FetchFriendPhoto(int FriendIndex)




int Facebook::CurUser_UserPhoto_GetSize()
{
	if (m_pCurUser == NULL)
		return -1;

	return (int) m_pCurUser->m_UserPhoto.size(); 

}//int Facebook::CurUser_UserPhoto_GetSize()



const std::string* Facebook::CurUser_UserPhoto_GetData()
{

	if (m_pCurUser == NULL)
		return NULL;

	return &(m_pCurUser->m_UserPhoto); 

}//const std::string* Facebook::CurUser_UserPhoto_GetData()



int Facebook::CurUser_FriendPhoto_GetSize(int FriendIndex)
{
	if (m_pCurUser == NULL)
		return -1;

	if (FriendIndex < 0 || FriendIndex >= CurUser_GetFriendsCount())
		return -1;

	return (int) m_pCurUser->m_FriendsPhotos.at(FriendIndex).size();

}//int Facebook::CurUser_FriendPhoto_GetSize(int FriendIndex)




const std::string* Facebook::CurUser_FriendPhoto_GetData(int FriendIndex)
{

	if (m_pCurUser == NULL)
		return NULL;

	if (FriendIndex < 0 || FriendIndex >= CurUser_GetFriendsCount())
		return NULL;

	return &(m_pCurUser->m_FriendsPhotos.at(FriendIndex)); 

}//const std::string* Facebook::CurUser_FriendPhoto_GetData(int FriendIndex)




int Facebook::CurUser_UserPhoto_GetWidth()
{
	if (m_pCurUser == NULL)
		return 0;

	return m_pCurUser->m_UserProfileInfo.PhotoSize.Width;   
}



int Facebook::CurUser_UserPhoto_GetHeight()
{
	if (m_pCurUser == NULL)
		return 0;

	return m_pCurUser->m_UserProfileInfo.PhotoSize.Height;   
}



int Facebook::CurUser_FriendPhoto_GetWidth(int FriendIndex)
{
	if (m_pCurUser == NULL)
		return 0;

	if (FriendIndex < 0 || FriendIndex >= CurUser_GetFriendsCount())
		return 0;

	return m_pCurUser->m_FriendsProfileInfo.at(FriendIndex).PhotoSize.Width;
}



int Facebook::CurUser_FriendPhoto_GetHeight(int FriendIndex)
{
	if (m_pCurUser == NULL)
		return 0;

	if (FriendIndex < 0 || FriendIndex >= CurUser_GetFriendsCount())
		return 0;

	return m_pCurUser->m_FriendsProfileInfo.at(FriendIndex).PhotoSize.Height;
}



//app and non-app friends routines

int Facebook::CurUser_AppFriends_GetCount()
{
	if (m_pCurUser == NULL)
		return -1;

	return (int)m_pCurUser->m_AppFriendsIndexes.size();

}



int Facebook::CurUser_AppFriends_GetCommonIndex(int AppFriendIndex)
{
	if (m_pCurUser == NULL)
		return -1;

	if (m_pCurUser->m_AppFriendsIndexes.empty() == true)
		return -1;

	if (AppFriendIndex < 0 || AppFriendIndex >= (int)m_pCurUser->m_AppFriendsIndexes.size())
		return -1;

	return m_pCurUser->m_AppFriendsIndexes.at(AppFriendIndex); 

}



int Facebook::CurUser_NonAppFriends_GetCount()
{
	if (m_pCurUser == NULL)
		return -1;

	return (int)m_pCurUser->m_NonAppFriendsIndexes.size();

}



int Facebook::CurUser_NonAppFriends_GetCommonIndex(int NonAppFriendIndex)
{
	if (m_pCurUser == NULL)
		return -1;

	if (m_pCurUser->m_NonAppFriendsIndexes.empty() == true)
		return -1;

	if (NonAppFriendIndex < 0 || NonAppFriendIndex >= (int)m_pCurUser->m_NonAppFriendsIndexes.size())
		return -1;

	return m_pCurUser->m_NonAppFriendsIndexes.at(NonAppFriendIndex); 

}





//notification and news feed posting routines
bool Facebook::CurUser_SendNotification(std::list<std::string>& UserIDs, const std::string& NotificationFBML)
{

	//checking if init was called
	if (m_AppKey.size() < 1)
	{
		m_ErrorDescription = "Facebook::CurUser_SendNotification(). Facebook::Init() wasn't called!"; return false;
	}

	//checking if cur user is ok
	if (m_pCurUser == NULL)
	{
		m_ErrorDescription = "Facebook::CurUser_SendNotification(). No current user was set!"; return false;
	}

	if (m_pCurUser->m_LoggedIn == false)
	{
		m_ErrorDescription = "Facebook::CurUser_SendNotification(). Current user is not logged in!"; return false;
	}

	//validating UserIDs list
	if (UserIDs.empty() == true)
	{
		m_ErrorDescription = "Facebook::CurUser_SendNotification(). User IDs list is empty!"; return false;
	}

	//validating notification FBML
	if (NotificationFBML.size() < 1)
	{
		m_ErrorDescription = "Facebook::CurUser_SendNotification(). Notification FBML string is empty!"; return false;
	}


	//no we may proceed
	std::list<std::string> Params;
	std::string XmlResult;
	
	//preparing params
	Params.push_back("notification=" + NotificationFBML);
	
	std::string UsersIDsString = "to_ids=";

	std::list<std::string>::iterator Iter;

	for (Iter = UserIDs.begin(); Iter != UserIDs.end(); Iter++)
	{
		UsersIDsString.append((*Iter));
		UsersIDsString.append(",");
	}

	UsersIDsString.resize(UsersIDsString.size() - 1); 
	
	Params.push_back(UsersIDsString); 

	bool RequestResult = HelperRequest_CallAPIMethod("facebook.notifications.send", Params, XmlResult); 

	if (RequestResult == false || XmlResult.size() < 1)
	{
		m_ErrorDescription = "Facebook::CurUser_SendNotification(). Request failed!";
		return false;
	}

	//success
	m_ErrorDescription = "";
	return true;

}//bool Facebook::CurUser_SendNotification(std::list<std::string>& UserIDs, const std::string& NotificationFBML)





bool Facebook::CurUser_PostFeedItem(FACEBOOK_FEEDITEM ItemType, const FacebookFeedItem& FeedItemInfo)
{
	//checking if init was called
	if (m_AppKey.size() < 1)
	{
		m_ErrorDescription = "Facebook::CurUser_PostFeedItem(). Facebook::Init() wasn't called!"; return false;
	}

	//checking if cur user is ok
	if (m_pCurUser == NULL)
	{
		m_ErrorDescription = "Facebook::CurUser_PostFeedItem(). No current user was set!"; return false;
	}

	if (m_pCurUser->m_LoggedIn == false)
	{
		m_ErrorDescription = "Facebook::CurUser_PostFeedItem(). Current user is not logged in!"; return false;
	}

	//validating FeedItemInfo
	if (FeedItemInfo.m_TitleFBML.size() < 1)
	{
		m_ErrorDescription = "Facebook::CurUser_PostFeedItem(). Invalid FeedItemInfo - title is missing!"; return false;
	}

	//no we may proceed
	std::list<std::string> Params;
	std::string XmlResult;

	//aggregating params

	//title
	Params.push_back("title=" + FeedItemInfo.m_TitleFBML);

	//body
	if (FeedItemInfo.m_BodyFBML.size() > 0)
		Params.push_back("body=" + FeedItemInfo.m_BodyFBML);

	//image 1
	if (FeedItemInfo.m_Image1URL.size() > 0)
	{
		Params.push_back("image_1=" + FeedItemInfo.m_Image1URL);

		if (FeedItemInfo.m_Image1DestinationURL.size() > 0)
			Params.push_back("image_1_link=" + FeedItemInfo.m_Image1DestinationURL);
	}

	//image 2
	if (FeedItemInfo.m_Image2URL.size() > 0)
	{
		Params.push_back("image_2=" + FeedItemInfo.m_Image2URL);

		if (FeedItemInfo.m_Image2DestinationURL.size() > 0)
			Params.push_back("image_2_link=" + FeedItemInfo.m_Image2DestinationURL);
	}

	//image 3
	if (FeedItemInfo.m_Image3URL.size() > 0)
	{
		Params.push_back("image_3=" + FeedItemInfo.m_Image3URL);

		if (FeedItemInfo.m_Image3DestinationURL.size() > 0)
			Params.push_back("image_3_link=" + FeedItemInfo.m_Image3DestinationURL);
	}

	//image 4
	if (FeedItemInfo.m_Image4URL.size() > 0)
	{
		Params.push_back("image_4=" + FeedItemInfo.m_Image4URL);

		if (FeedItemInfo.m_Image4DestinationURL.size() > 0)
			Params.push_back("image_4_link=" + FeedItemInfo.m_Image4DestinationURL);
	}


	bool RequestResult;

	switch(ItemType)
	{
		case FBFEEDITEM_ACTION:
			{
				RequestResult = HelperRequest_CallAPIMethod("facebook.feed.publishActionOfUser", Params, XmlResult); 
			}
			break;
		case FBFEEDITEM_STORY:
			{
				RequestResult = HelperRequest_CallAPIMethod("facebook.feed.publishStoryToUser", Params, XmlResult); 
			}
			break;
	}

	if (RequestResult == false || XmlResult.size() < 1)
	{
		m_ErrorDescription = "Facebook::CurUser_PostFeedItem(). Request failed!";
		return false;
	}

	std::string::size_type FindLoc = XmlResult.find("feed_publish");

	if (FindLoc != std::string::npos)
	{
		m_ErrorDescription = "";
		return true;
	}
	else
	{
		m_ErrorDescription = "Facebook::CurUser_PostFeedItem(). Request failed. Possible because of invalid params.";
		return false;
	}

}//bool Facebook::CurUser_PostFeedItem(FACEBOOK_FEEDITEM ItemType, const FacebookFeedItem& FeedItemInfo)




